# ==============================================================================
# The MIT License
#
# Copyright (c) 2012 Sky Morey
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# ==============================================================================

param ($command,
	$packageName = '',
	$source = 'https://go.microsoft.com/fwlink/?LinkID=206669',
	$version = '',
	[alias("all")][switch]$allVersions = $false,
	[alias("ia","installArgs")][string]$installArguments = '',
	[alias("o","override","overrideArguments","notSilent")][switch]$overrideArgs = $false)
	#todo:,[switch]$silent)

# Set the culture to invariant
$culture = [Globalization.CultureInfo]::InvariantCulture;
$currentThread = [Threading.Thread]::CurrentThread;
$currentThread.CurrentCulture = $culture;
$currentThread.CurrentUICulture = $culture;

#Let's get Chocolatey!
$chocVer = '0.9.8.13'
$nugetChocolateyPath = (Split-Path -parent $MyInvocation.MyCommand.Definition)
$dagentPath = (Split-Path -Parent $nugetChocolateyPath)
$dagentExePath = Join-Path $nuGetPath 'bin'
$nugetLibPath = Join-Path $nuGetPath 'lib'
$chocInstallVariableName = "ChocolateyInstall"

$nugetExe = Join-Path $nugetChocolateyPath 'nuget.exe'
$h1 = '====================================================='
$h2 = '-------------------------'

function Run-Process { param ([string]$file, [string]$arguments = $args, [switch]$elevated);
	Write-Host "Running $file $arguments. This may take awhile and permissions may need to be elevated, depending on the package.";
	$psi = new-object Diagnostics.ProcessStartInfo $file;
	$psi.Arguments = $arguments;
	#$psi.Verb = "runas";
	#$psi.CreateNoWindow = $true
	#$psi.RedirectStandardOutput = $true;
	#$psi.RedirectStandardError = $true;
	#$psi.UseShellExecute = $false;
	$psi.WorkingDirectory = get-location;
	$s = [Diagnostics.Process]::Start($psi);
	$s.WaitForExit();
	if ($s.ExitCode -ne 0) { Write-Host "[ERROR] Running $file with $arguments was not successful." -ForegroundColor White -BackgroundColor DarkRed }
}

function Dagent-Install { param ([string]$packageName, $source = 'https://go.microsoft.com/fwlink/?LinkID=206669', [string]$version = '', [string]$installerArguments = '')
	if ($($packageName).EndsWith('.config')) {
		Dagent-PackagesConfig $packageName
		return
	}
	Dagent-NuGet $packageName $source $version;
}

function Dagent-PackagesConfig { param ([Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][string]$packagesConfigPath)
	if (-not(Test-Path $packagesConfigPath)) {
		if (-not($($packagesConfigPath).Contains('\'))) { Dagent-NuGet $packagesConfigPath }
		return
	}
	$h1
	"Installing packages from manifest: '$(Resolve-Path $packagesConfigPath)'"
	$h1
	$xml = [xml] (Get-Content $packagesConfigPath)
	$xml.packages.package | ?{ $_.id -ne '' -and $_.id -ne $null} | %{ Dagent-Install -packageName $_.id -source $_.source -version $_.version }
}

function Dagent-NuGet { param ([string]$packageName, $source = 'https://go.microsoft.com/fwlink/?LinkID=206669')
	$srcArgs = "$source"
	if ($source -like 'https://go.microsoft.com/fwlink/?LinkID=206669') { $srcArgs = "http://chocolatey.org/api/feeds/ OR $source" }
	@"
"@ | Write-Host
	$nugetOutput = Run-NuGet $packageName $source $version
	#
	foreach ($line in $nugetOutput) {
		if ($line -notlike "*not installed*" -and $line -notlike "Dependency*already installed." -and $line -notlike "Attempting to resolve dependency*") {
			$installedPackageName = ''
			$installedPackageVersion = ''
			#
			$regex = [regex]"'[.\S]+\s?"
			$pkgNameMatches = $regex.Matches($line) | select -First 1 
			if ($pkgNameMatches -ne $null) { $installedPackageName = $pkgNameMatches -replace "'", "" -replace " ", "" }
			#
			$regex = [regex]"[0-9.]+[[)]?'"
			$pkgVersionMatches = $regex.Matches($line) | select -First 1 
			if ($pkgVersionMatches -ne $null) { $installedPackageVersion = $pkgVersionMatches -replace '\)', '' -replace "'", "" -replace " ", "" }
			#
			if ($installedPackageName -eq '') {
				$regex = [regex]"`"[.\S]+\s?"
				$pkgNameMatches = $regex.Matches($line) | select -First 1
				$installedPackageName = $pkgNameMatches -replace "`"", "" -replace " ", ""
				$installedPackageVersion = $version
			}
			if ($installedPackageName -ne '') {
				$packageFolder = ''
				if ($installedPackageVersion -ne '') { $packageFolder = Join-Path $nugetLibPath "$($installedPackageName).$($installedPackageVersion)" } 
				else {
					#search the lib directory for the highest number of the folder        
					$packageFolder = Get-ChildItem $nugetLibPath | ?{$_.name -match "^$installedPackageName*"} | sort name -Descending | select -First 1 
					$packageFolder = $packageFolder.FullName
				}
				if ($packageFolder -ne '') {
@"
$h2
$h2
Chocolatey Runner ($($installedPackageName.ToUpper()))
$h2
"@ | Write-Host
				if ([IO.Directory]::Exists($packageFolder)) {
					Delete-ExistingErrorLog $installedPackageName
					Run-ChocolateyPS1 $packageFolder $installedPackageName $installerArguments
					Get-ChocolateyBins $packageFolder
				}
			}
		}
	}
}
  
@"
$h1
Chocolatey has finished installing `'$packageName`' - check log for errors.
$h1
"@ | Write-Host
}

function Delete-ExistingErrorLog {
param([string] $packageName)
  $chocTempDir = Join-Path $env:TEMP "chocolatey"
  $tempDir = Join-Path $chocTempDir "$packageName"
  $failureLog = Join-Path $tempDir 'failure.log'
  if ([System.IO.File]::Exists($failureLog)) {
    [System.IO.File]::Delete($failureLog)
  }
}

function Run-NuGet {
param([string] $packageName, $source = 'https://go.microsoft.com/fwlink/?LinkID=206669',$version = '')
@"
$h2
NuGet
$h2
"@ | Write-Host

  $srcArgs = "/source `"$source`""
  if ($source -like 'https://go.microsoft.com/fwlink/?LinkID=206669') {
    $srcArgs = "/source http://chocolatey.org/api/feeds/ /source $source"
  }

  $packageArgs = "install $packageName /outputdirectory `"$nugetLibPath`" $srcArgs"
  if ($version -notlike '') {
    $packageArgs = $packageArgs + " /version $version";
  }
  $logFile = Join-Path $nugetChocolateyPath 'install.log'
  $errorLogFile = Join-Path $nugetChocolateyPath 'error.log'
  Start-Process $nugetExe -ArgumentList $packageArgs -NoNewWindow -Wait -RedirectStandardOutput $logFile -RedirectStandardError $errorLogFile

  $nugetOutput = Get-Content $logFile -Encoding Ascii
  foreach ($line in $nugetOutput) {
    Write-Host $line
  }
  $errors = Get-Content $errorLogFile
  if ($errors -ne '') {
    Write-Host $errors -BackgroundColor Red -ForegroundColor White
    #Throw $errors
  }
  
  if (($nugetOutput -eq '' -or $nugetOutput -eq $null) -and ($errors -eq '' -or $errors -eq $null)) {
    $noExecution = 'Execution of NuGet not detected. Please make sure you have .NET Framework 4.0 installed.'
    #write-host  -BackgroundColor Red -ForegroundColor White
    Throw $noExecution
  }
  
  return $nugetOutput
}

function Run-ChocolateyPS1 {
param([string] $packageFolder, [string] $packageName)
  if ($packageFolder -notlike '') { 
@"
$h2
Chocolatey Installation (chocolateyinstall.ps1)
$h2
Looking for chocolateyinstall.ps1 in folder $packageFolder
If chocolateyInstall.ps1 is found, it will be run.
$h2
"@ | Write-Host

    $ps1 = Get-ChildItem  $packageFolder -recurse | ?{$_.name -match "chocolateyinstall.ps1"} | sort name -Descending | select -First 1
    
    if ($ps1 -notlike '') {
      $env:chocolateyInstallArguments = "$installArguments"
      $env:chocolateyInstallOverride = $null
      if ($overrideArgs -eq $true) {
        $env:chocolateyInstallOverride = $true
      } 
      
      $ps1FullPath = $ps1.FullName
      & "$ps1FullPath"
      $env:chocolateyInstallArguments = ''
      $env:chocolateyInstallOverride = $null
      
      # $importChocolateyHelpers = "";
      # Get-ChildItem "$nugetChocolateyPath\helpers" -Filter *.psm1 | ForEach-Object { $importChocolateyHelpers = "& import-module -name  `'$($_.FullName)`';$importChocolateyHelpers" };
      # Run-ChocolateyProcess powershell "-NoProfile -ExecutionPolicy unrestricted -Command `"$importChocolateyHelpers & `'$ps1FullPath`'`"" -elevated
      ##testing Start-Process -FilePath "powershell.exe" -ArgumentList " -noexit `"$ps1FullPath`"" -Verb "runas"  -Wait  #-PassThru -UseNewEnvironment ##-RedirectStandardError $errorLog -WindowStyle Normal
      
      #detect errors
      $chocTempDir = Join-Path $env:TEMP "chocolatey"
      $tempDir = Join-Path $chocTempDir "$packageName"
      $failureLog = Join-Path $tempDir 'failure.log'
      if ([System.IO.File]::Exists($failureLog)) {
        #we have issues, time to throw an error
        $errorContents = Get-Content $failureLog
        if ($errorContents -ne '') {
          foreach ($errorLine in $errorContents) {
            Write-Host $errorLine -BackgroundColor Red -ForegroundColor White
          }
          throw $errorContents
        }
      }
    }
  }
}

function Get-ChocolateyBins {
param([string] $packageFolder)
  if ($packageFolder -notlike '') { 
@"
$h2
Executable Batch Links
$h2
Looking for executables in folder: $packageFolder
Adding batch files for any executables found to a location on PATH. In other words the executable will be available from ANY command line/powershell prompt.
$h2
"@ | Write-Host
    $batchCreated = $false
    try {
      $files = get-childitem $packageFolder -include *.exe -recurse
      foreach ($file in $files) {
        if (!(test-path($file.FullName + '.ignore'))) {
          Generate-BinFile $file.Name.Replace(".exe","").Replace(".EXE","") $file.FullName
          $batchCreated = $true
        }
      }
    }
    catch {
      #Write-Host 'There are no executables (that are not ignored) in the package.'
    }
    
    if (!($batchCreated)) {
      Write-Host 'There are no executables (that are not ignored) in the package.'
    }
    
    Write-Host "$h2"
  }
}

function Generate-BinFile {
param([string] $name, [string] $path)
  $packageBatchFileName = Join-Path $nugetExePath "$name.bat"
  $path = $path.ToLower().Replace($nugetPath.ToLower(), "%DIR%..\").Replace("\\","\")
  Write-Host "Adding $packageBatchFileName and pointing to $path"
"@echo off
SET DIR=%~dp0%
""$path"" %*" | Out-File $packageBatchFileName -encoding ASCII 
}

function Dagent-Update { param ([string]$packageName ='', $source = 'https://go.microsoft.com/fwlink/?LinkID=206669')
	if ($packageName -eq '') { $packageName = 'Dagent'; }
	$packages = $packageName
	if ($packageName -eq 'all') {
		$packageFolders = Get-ChildItem $nugetLibPath | sort name
		$packages = $packageFolders -replace "(\.\d{1,})+"|gu 
	}
	foreach ($package in $packages) {
		$versions = Dagent-Version $package $source
		if ($versions -ne $null -and $versions.'foundCompare' -lt $versions.'latestCompare') { Dagent-NuGet $package $source }
	}
}

function Dagent-Help {
	@"
$h1
HELP
$h1
"@ | Write-Host
}

function Dagent-Version { param ([string]$packageName='', [string]$source='https://go.microsoft.com/fwlink/?LinkID=206669')
	if ($packageName -eq '') { $packageName = 'Dagent'; }
	$packages = $packageName
	if ($packageName -eq 'all') {
		$packageFolders = Get-ChildItem $nugetLibPath | sort name
		$packages = $packageFolders -replace "(\.\d{1,})+"|gu 
	}
	$srcArgs = "/source $source"
	if ($source -like 'https://go.microsoft.com/fwlink/?LinkID=206669') { $srcArgs = "/source http://chocolatey.org/api/feeds/ /source $source" }
	foreach ($package in $packages) {
		$logFile = Join-Path $nugetChocolateyPath 'list.log'
		Start-Process $nugetExe -ArgumentList "list ""$package"" $srcArgs" -NoNewWindow -Wait -RedirectStandardOutput $logFile
		Start-Sleep 1 #let it finish writing to the config file
		$versionLatest = Get-Content $logFile | ?{$_ -match "^$package\s+\d+"} | sort $_ -Descending | select -First 1 
		$versionLatest = $versionLatest -replace "$package ", "";
		$versionLatestCompare = $versionLatest.Split('.') | %{('0' * (5 - $_.Length)) + $_} 
		$versionLatestCompare = [String]::Join('.', $versionLatestCompare)
		$versionFound = $chocVer
		if ($packageName -ne 'chocolatey') {
			$versionFound = 'no version'
			$packageFolder = Get-ChildItem $nugetLibPath | ?{$_.name -match "^$package\.\d+"} | sort name -Descending | select -First 1 
			if ($packageFolder -notlike '') { 
				#Write-Host $packageFolder
				$versionFound = $packageFolder.Name -replace "$package\."
			}
		}
		#
		$versionFoundCompare = ''
		if ($versionFound -ne 'no version') {
			$versionFoundCompare = $versionFound.Split('.') | %{('0' * (5 - $_.Length)) + $_} 
			$versionFoundCompare = [String]::Join('.', $versionFoundCompare)
		}    
		#
		$verMessage = "The most recent version of $package available from ($source) is $versionLatest. On your machine you have $versionFound installed."
		if ($versionLatest -eq $versionFound) { $verMessage = "You have the latest version of $package ($versionLatest) based on ($source)." }
		if ($versionLatestCompare -lt $versionFoundCompare) { $verMessage = "$verMessage You must be smarter than the average bear..." }
		if ($versionLatest -eq '') { $verMessage = "$package does not appear to be on ($source). You have $versionFound installed. Interesting..." }
		Write-Host $verMessage
	}
	$versions = @{latest = $versionLatest; found = $versionFound; latestCompare = $versionLatestCompare; foundCompare = $versionFoundCompare; }
	return $versions
}

function Dagent-InstallIfMissing { param ([string]$packageName, $source = 'https://go.microsoft.com/fwlink/?LinkID=206669', $version = '')
	$versions = Chocolatey-Version $packageName $source
	if ($versions.'found' -contains 'no version' -or ($version -ne '' -and $versions.'found' -ne $version)) { Dagent-NuGet $packageName $source $version }
}

# main
Remove-LastInstallLog
switch -wildcard ($command) 
{
	"install" { Dagent-Install $packageName $source $version $installArguments; }
	"installmissing" { Dagent-InstallIfMissing $packageName $source $version; }
	"update" { Dagent-Update $packageName $source; }
	"list" { Dagent-List $packageName $source; }
	"version" { Dagent-Version $packageName $source; }
	default { Dagent-Help; }
}
